﻿using MasterMasterMode.Common.Systems;
using Microsoft.Xna.Framework;
using System;
using Terraria;
using Terraria.Audio;
using Terraria.GameContent.Bestiary;
using Terraria.GameContent.ItemDropRules;
using Terraria.ID;
using Terraria.ModLoader;

namespace MasterMasterMode.Content.NPCs
{
    public class MagicalHarp : ModNPC
    {
        public enum AIState
        {
            Idle,
            PlayerDetected,
            Float,
            Approach,
            SpinFast
        }
        public override void SetBestiary(BestiaryDatabase database, BestiaryEntry bestiaryEntry)
        {
            bestiaryEntry.Info.AddRange([
                BestiaryDatabaseNPCsPopulator.CommonTags.SpawnConditions.Biomes.Underground,
                new FlavorTextBestiaryInfoElement("Mods.MasterMasterMode.Bestiary.MagicalHarp"),
            ]);
        }

        public AIState currentState = AIState.Idle;
        public int stateTimer = 0;
        public int attackTimer = 0;
        public int spinSpeedLevel = 0;
        public float baseRotation = 0f;
        public float floatOffset = 0f;
        public Vector2 originalPosition = Vector2.Zero;
        public int playerOutOfRangeTimer = 0;
        public const int ReturnToIdleDelay = 600;
        public const float DetectionRange = 400f;
        public const float FloatAmplitude = 25f;
        public const float FloatSpeed = 0.04f;
        public const float BaseSpinSpeed = 0.03f;
        public const float SpinFastSpinSpeed = 0.15f;
        public const int IdleShootInterval = 80;
        public const int FloatDuration = 90;
        public const int ApproachDuration = 120;
        public const int SpinFastDuration = 1800;
        public const float MinHeightAboveGround = 300f;
        public const int SpinFastShootInterval = 2;
        public const float SpinFastProjectileSpeed = 15f;
        public const float ProjectileSpacing = 2f;

        public override void SetStaticDefaults()
        {
            Main.npcFrameCount[Type] = 1;
        }

        public override void SetDefaults()
        {
            NPC.width = 40;
            NPC.height = 40;
            NPC.lifeMax = 200;
            NPC.damage = 40;
            NPC.defense = 20;
            NPC.knockBackResist = 0f;
            NPC.noGravity = true;
            NPC.noTileCollide = true;
            NPC.HitSound = SoundID.NPCHit7;
            NPC.DeathSound = SoundID.Item26;
            NPC.value = Item.buyPrice(0, 0, 13, 0);
            NPC.aiStyle = -1;
            Music = MusicID.Plantera;
            NPC.boss= true;
        }
        public override void ModifyNPCLoot(NPCLoot npcLoot)
        {
            npcLoot.Add(ItemDropRule.Common(ItemID.MagicalHarp, 1, 1, 1));           
        }

        public override void AI()
        {
            NPC.TargetClosest();
            Player player = Main.player[NPC.target];

            if (!player.active || player.dead)
            {
                ReturnToIdle();
                return;
            }
            MaintainAltitude();
            stateTimer++;
            attackTimer++;
            float distanceToPlayer = Vector2.Distance(player.Center, NPC.Center);
            if (distanceToPlayer > DetectionRange * 1.5f && currentState != AIState.Idle)
            {
                playerOutOfRangeTimer++;
                if (playerOutOfRangeTimer > ReturnToIdleDelay)
                {
                    ReturnToIdle();
                    return;
                }
            }
            else
            {
                playerOutOfRangeTimer = 0;
            }

            switch (currentState)
            {
                case AIState.Idle:
                    IdleAI(player, distanceToPlayer);
                    break;

                case AIState.PlayerDetected:
                    PlayerDetectedAI(player, distanceToPlayer);
                    break;

                case AIState.Float:
                    FloatAI(player, distanceToPlayer);
                    break;

                case AIState.Approach:
                    ApproachAI(player, distanceToPlayer);
                    break;

                case AIState.SpinFast:
                    SpinFastAI(player, distanceToPlayer);
                    break;
            }
            UpdateRotation();
        }

        public void MaintainAltitude()
        {
            float groundLevel = FindGroundLevel();
            if (NPC.Center.Y > groundLevel - MinHeightAboveGround)
            {
                NPC.velocity.Y = MathHelper.Lerp(NPC.velocity.Y, -2f, 0.1f);
            }
            else
            {
                NPC.velocity.Y = MathHelper.Lerp(NPC.velocity.Y, 0f, 0.1f);
            }
        }

        public float FindGroundLevel()
        {
            int tileX = (int)(NPC.Center.X / 16);
            int tileY = (int)(NPC.Center.Y / 16);

            for (int y = tileY; y < Main.maxTilesY; y++)
            {
                if (WorldGen.SolidTile(tileX, y))
                {
                    return y * 16;
                }
            }
            return Main.maxTilesY * 16;
        }

        public void IdleAI(Player player, float distance)
        {
            if (stateTimer % 150 == 0)
            {
                Vector2 randomDirection = new Vector2(Main.rand.NextFloat(-1f, 1f), Main.rand.NextFloat(-0.5f, -1f)).SafeNormalize(Vector2.Zero);
                NPC.velocity = randomDirection * 1.5f;
            }

            NPC.velocity *= 0.97f;
            if (attackTimer > IdleShootInterval)
            {
                ShootProjectileIdle();
                attackTimer = 0;
            }
            baseRotation += BaseSpinSpeed * 0.5f;
            if (distance < DetectionRange)
            {
                currentState = AIState.PlayerDetected;
                stateTimer = 0;
                originalPosition = NPC.Center;
                playerOutOfRangeTimer = 0;
            }
        }
        public void PlayerDetectedAI(Player player, float distance)
        {
            NPC.velocity *= 0.95f;
            if (stateTimer > 40)
            {
                currentState = AIState.Float;
                stateTimer = 0;
                attackTimer = 0;
            }
        }
        public void FloatAI(Player player, float distance)
        {
            floatOffset = (float)Math.Sin(stateTimer * FloatSpeed) * FloatAmplitude;
            NPC.Center = originalPosition + new Vector2(0, floatOffset);
            NPC.velocity.X *= 0.9f;
            if (attackTimer > 35)
            {
                ShootProjectileRandom();
                attackTimer = 0;
            }
            baseRotation += BaseSpinSpeed * 1.2f;
            if (stateTimer > FloatDuration)
            {
                currentState = AIState.Approach;
                stateTimer = 0;
                attackTimer = 0;
            }
        }
        public void ApproachAI(Player player, float distance)
        {
            Vector2 targetPosition = player.Center + new Vector2(0, -150f);
            Vector2 direction = (targetPosition - NPC.Center).SafeNormalize(Vector2.Zero);

            NPC.velocity = Vector2.Lerp(NPC.velocity, direction * 2.5f, 0.03f);
            if (attackTimer > 25)
            {
                ShootProjectileRandom();
                attackTimer = 0;
            }
            baseRotation += BaseSpinSpeed * 1.8f;
            if (stateTimer > ApproachDuration)
            {
                currentState = AIState.SpinFast;
                stateTimer = 0;
                attackTimer = 0;
                spinSpeedLevel = 1;
            }
        }
        public void SpinFastAI(Player player, float distance)
        {
            NPC.velocity *= 0.98f;
            baseRotation += SpinFastSpinSpeed;
            if (attackTimer >= SpinFastShootInterval)
            {
                ShootTwinProjectiles();
                attackTimer = 0;
            }
            if (stateTimer > SpinFastDuration)
            {
                currentState = AIState.Float;
                stateTimer = 0;
                attackTimer = 0;
                spinSpeedLevel = 0;
                originalPosition = NPC.Center;
            }
        }
        public void ReturnToIdle()
        {
            currentState = AIState.Idle;
            stateTimer = 0;
            attackTimer = 0;
            spinSpeedLevel = 0;
            baseRotation = 0f;
            floatOffset = 0f;
            playerOutOfRangeTimer = 0;
        }
        public void UpdateRotation()
        {
            NPC.rotation = baseRotation;
        }
        public void ShootProjectileIdle()
        {
            float angle = baseRotation + Main.rand.NextFloat(-0.3f, 0.3f);
            Vector2 direction = new((float)Math.Cos(angle), (float)Math.Sin(angle));
            ShootProjectile(direction, 5f);
        }

        public void ShootProjectileRandom()
        {
            Vector2 randomDirection = new Vector2(Main.rand.NextFloat(-1f, 1f), Main.rand.NextFloat(-1f, 1f)).SafeNormalize(Vector2.Zero);
            float speed = 6f + spinSpeedLevel * 0.5f;
            ShootProjectile(randomDirection, speed);
        }

        public void ShootProjectileSpin()
        {
            float angle = baseRotation + Main.rand.NextFloat(-0.1f, 0.1f);
            Vector2 direction = new((float)Math.Cos(angle), (float)Math.Sin(angle));
            float speed = 7f + baseRotation % MathHelper.TwoPi * 0.5f;
            ShootProjectile(direction, speed);
        }
        public void ShootTwinProjectiles()
        {
            Vector2 mainDirection = new((float)Math.Cos(baseRotation), (float)Math.Sin(baseRotation));
            Vector2 perpendicular = new(-mainDirection.Y, mainDirection.X);
            float speed = SpinFastProjectileSpeed;
            Vector2 leftPosition = NPC.Center - perpendicular * ProjectileSpacing * 0.5f;
            ShootProjectileAtPosition(mainDirection, speed, leftPosition);
            Vector2 rightPosition = NPC.Center + perpendicular * ProjectileSpacing * 0.5f;
            ShootProjectileAtPosition(mainDirection, speed, rightPosition);
        }

        public void ShootProjectile(Vector2 direction, float speed)
        {
            Vector2 shootPosition = NPC.Center + direction * 20f;
            ShootProjectileAtPosition(direction, speed, shootPosition);
        }

        public void ShootProjectileAtPosition(Vector2 direction, float speed, Vector2 position)
        {
            int projectileType = Main.rand.Next(3) switch { 0 => 76, 1 => 77, _ => 78 };
            if (Main.netMode != NetmodeID.MultiplayerClient)
            {
                int proj = Projectile.NewProjectile(NPC.GetSource_FromAI(), position, direction * speed, projectileType, NPC.damage / 2, 2f, Main.myPlayer);
                Main.projectile[proj].friendly = false;
                Main.projectile[proj].hostile = true;
                Main.projectile[proj].timeLeft = 300;
                Main.projectile[proj].rotation = direction.ToRotation();
            }
            SoundEngine.PlaySound(SoundID.Item26, position);
        }

        public override void HitEffect(NPC.HitInfo hit)
        {
            for (int i = 0; i < 8; i++)
            {
                Dust.NewDust(NPC.position, NPC.width, NPC.height, DustID.Gold, hit.HitDirection * 2f, -2f, 0, default, 1.3f);
            }
        }

        public override void OnKill()
        {
            for (int i = 0; i < 25; i++)
            {
                Dust.NewDust(NPC.position, NPC.width, NPC.height, DustID.Gold, Main.rand.NextFloat(-4f, 4f), Main.rand.NextFloat(-4f, 2f), 0, default, 1.6f);
            }
            SoundEngine.PlaySound(SoundID.Item26, NPC.Center);
        }
        public override float SpawnChance(NPCSpawnInfo spawnInfo)
        {
            if (ModContent.GetInstance<MasterMasterModeSystem>().MasterMasterMode)
            {
                if (spawnInfo.Player.ZoneHallow && spawnInfo.Player.ZoneNormalUnderground && Main.hardMode)
                {
                    return 0.2f;
                }
            }
            return 0f;
        }

    }
}